﻿using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Pyramid.VsOnline.Core.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pyramid.VsOnline.Core.Services
{
    public sealed class TfsManager
    {
        private TfsTeamProjectCollection CurrentTfsTeamProjectCollection { get; set; }
        private VersionControlServer VersionControlServer { get; set; }
        public delegate void NonFataErrorHandler(Object sender, ExceptionEventArgs e);
        public event NonFataErrorHandler OnNonFatalError;
        public bool Get(TfsParameters tfsParameters)
        {
            throw new NotImplementedException();
        }
        public string GetWorkspaces(TfsParameters tfsParameters)
        {
            if(!tfsParameters.IsWorkspaces)
            {
                throw new MissingArgumentException("Workspace parameter not found!");
            }
            if (tfsParameters.Workspaces == null)
            {
                throw new MissingArgumentException("Workspace parameter arguments missing!");
            }
            ConnectTfsServer(tfsParameters);
            var workspaces = this.VersionControlServer.QueryWorkspaces(tfsParameters.Workspaces.WorkspaceName, tfsParameters.Workspaces.Owner, tfsParameters.Workspaces.Computer);
            if(workspaces==null || workspaces.Length ==0)
            {
                throw new TfsResultException("No workspaces found");
            }
            string result = "\nCollection: " + tfsParameters.ServerUrl + "\n";
            result += "Workspace                    Owner                   Computer        Comment         \n";
            result += "--------------------------   ----------------------  --------------- ----------------\n";
            string rowFormat = "{0}             {1}             {2}         {3}";
            foreach(var workspace in workspaces)
            {
                result += string.Format(rowFormat,workspace.Name, workspace.OwnerDisplayName, workspace.Computer, workspace.Comment) + "\n";
            }
            result += "\n\n";
            return result;
        }

        public string CreateWorkspace(TfsParameters tfsParameters)
        {
            if (!tfsParameters.IsCreateWorkspace)
            {
                throw new MissingArgumentException("Create Workspace parameter not found!");
            }
            if (tfsParameters.WorkspaceCreate == null)
            {
                throw new MissingArgumentException("Workspace Create parameter arguments missing!");
            }
            ConnectTfsServer(tfsParameters);
            var workspaces = this.VersionControlServer.QueryWorkspaces(tfsParameters.WorkspaceCreate.WorkspaceName, tfsParameters.WorkspaceCreate.Owner, tfsParameters.WorkspaceCreate.Computer);
            if (workspaces == null || workspaces.Length == 0)
            {
                var workspace = this.VersionControlServer.CreateWorkspace(tfsParameters.WorkspaceCreate.WorkspaceName,
                    tfsParameters.WorkspaceCreate.Owner, "Created By Pyramid.VsOnline.TFGet.Exe");
                string result = "\nCollection: " + tfsParameters.ServerUrl + "\n";
                result += "Workspace                    Owner                   Computer        Comment         \n";
                result += "--------------------------   ----------------------  --------------- ----------------\n";
                string rowFormat = "{0}             {1}             {2}         {3}";
                result += string.Format(rowFormat, workspace.Name, workspace.OwnerDisplayName, workspace.Computer, workspace.Comment) + "\n";
                result += "\n\n";
                return result;
            }
            return "Ignored workspace creation. Workspace already exists!\n\n";
        }

        public string DeleteWorkspace(TfsParameters tfsParameters)
        {
            if (!tfsParameters.IsDeleteWorkspace)
            {
                throw new MissingArgumentException("Delete Workspace parameter not found!");
            }
            if (tfsParameters.WorkspaceDelete == null)
            {
                throw new MissingArgumentException("Workspace Delete parameter arguments missing!");
            }
            ConnectTfsServer(tfsParameters);
            var workspaces = this.VersionControlServer.QueryWorkspaces(tfsParameters.WorkspaceDelete.WorkspaceName, tfsParameters.WorkspaceDelete.Owner,null);
            if (workspaces != null && workspaces.Length > 0)
            {
                var isDeleted = this.VersionControlServer.DeleteWorkspace(tfsParameters.WorkspaceDelete.WorkspaceName,
                    tfsParameters.WorkspaceDelete.Owner);
                if(isDeleted)
                {
                    return "Workspace deleted: " + tfsParameters.WorkspaceDelete.WorkspaceName + "\n";
                }
                else
                {
                    return "Unable to delete Workspace: " + tfsParameters.WorkspaceDelete.WorkspaceName + "\n";
                }
            }
            return "Ignored workspace deletion. Workspace already exists!\n\n";
        }

        static string serverProjectPath = string.Empty;
        public string MapWorkspace(TfsParameters tfsParameters)
        {
            serverProjectPath = tfsParameters.Workfold.ServerProjectPath;
            if (!tfsParameters.IsWorkfold)
            {
                throw new MissingArgumentException("Workfold parameter not found!");
            }
            if (tfsParameters.Workfold == null)
            {
                throw new MissingArgumentException("Workfold parameter arguments missing!");
            }
            ConnectTfsServer(tfsParameters);
            var workspace = this.VersionControlServer.GetWorkspace(tfsParameters.Workfold.Workspace, tfsParameters.Workfold.Owner);
            var isLocalPathMapped = workspace.IsLocalPathMapped(tfsParameters.Workfold.TargetPath);
            if (!isLocalPathMapped)
            {
                try
                {
                    workspace.Map(tfsParameters.Workfold.ServerProjectPath, tfsParameters.Workfold.TargetPath);
                    return "\nWorkspace mapped to local path!\n\n";
                }
                catch(MappingConflictException ex)
                {
                    return "\n" + ex.Message + "\n\n";
                }
            }
            else
                return "\nWorkspace already mapped with local path!\n\n";
        }
        public void DownloadLatestFiles(TfsParameters tfsParameters)
        {
            ConnectTfsServer(tfsParameters);
            var files = this.VersionControlServer.GetItems(tfsParameters.Workfold.ServerProjectPath, VersionSpec.Latest, RecursionType.Full, DeletedState.Any, ItemType.Any);
            
            foreach (Item item in files.Items)
            {
                var localFilePath = GetLocalFilePath(tfsParameters.Workfold.ServerProjectPath, item, tfsParameters.Workfold.TargetPath);
                switch (item.ItemType)
                {
                    case ItemType.Any:
                        throw new ArgumentOutOfRangeException("ItemType.Any - not sure what to do with this");
                    case ItemType.File:
                        if (File.Exists(localFilePath))
                        {
                            if (item.DeletionId > 0)
                            {
                                File.Delete(localFilePath);
                                if (!tfsParameters.Silent)
                                    Console.WriteLine("Deleting from local: '{0}'", localFilePath);
                            }
                            else
                            {
                                var fInfo = new FileInfo(localFilePath);
                                if (fInfo.LastWriteTime < item.CheckinDate)
                                {
                                    //Console.WriteLine(fInfo.LastWriteTime.ToString("dd/MM/yyyy hh:mm:ss") + "   " + item.CheckinDate.ToString("dd/MM/yyyy hh:mm:ss"));
                                    item.DownloadFile(localFilePath);
                                    if (!tfsParameters.Silent)
                                        Console.WriteLine("Getting: '{0}'", localFilePath);
                                }
                            }
                        }
                        else if(item.DeletionId>0)
                        {
                            if (!tfsParameters.Silent)
                                Console.WriteLine("Getting: '{0}'", localFilePath);
                            item.DownloadFile(localFilePath);
                        }
                        break;
                    case ItemType.Folder:
                        if (!Directory.Exists(localFilePath))
                        {
                            if (!tfsParameters.Silent) Console.WriteLine("Creating Directory: {0}", localFilePath);
                            Directory.CreateDirectory(localFilePath);
                        }
                        break;
                }
            }
        }
        public void GetLatestVersion(TfsParameters tfsParameters)
        {
            if (!tfsParameters.IsGetLatestVersion)
            {
                throw new MissingArgumentException("Get parameter not found!");
            }
            if (tfsParameters.LatestVersion == null)
            {
                throw new MissingArgumentException("Get parameter arguments missing!");
            }
            ConnectTfsServer(tfsParameters);
            var allWorkspaces = this.VersionControlServer.QueryWorkspaces(null, tfsParameters.LoginName, null);
            var currentWorkspace = allWorkspaces.FirstOrDefault(ws => ws.Folders.Any(f => f.LocalItem.ToLower().Trim() == tfsParameters.LatestVersion.TargetPath.ToLower().Trim()));
            if (currentWorkspace != null)
            {
                currentWorkspace.Get(VersionSpec.Latest, GetOptions.GetAll | GetOptions.Overwrite);
                
            }
            else
            {
                throw new WorkspaceNotFoundException("Workspace not found for local path " + tfsParameters.LatestVersion.TargetPath);
            }
        }

        public string GetHistory(TfsParameters tfsParameters)
        {
            if (!tfsParameters.IsGetHistory)
            {
                throw new MissingArgumentException("get history parameter not found!");
            }
            if (tfsParameters.History == null)
            {
                throw new MissingArgumentException("History parameter arguments missing!");
            }
            ConnectTfsServer(tfsParameters);
            var response = string.Empty;
            var results = this.VersionControlServer.QueryHistory(tfsParameters.History.ServerProjectPath,
                                        VersionSpec.Latest,
                                        0,
                                        RecursionType.Full,                  /* recursive */
                                        null,
                                        new DateVersionSpec(tfsParameters.History.VersionFrom.GetValueOrDefault().AddDays(-100)),
                                        new DateVersionSpec(tfsParameters.History.ToVersion), /* /version:W */
                                        5,                                   /* /stopafter:1 */
                                        true,                                /* /format:detailed */
                                        true,
                                        true,
                                        false);
            var changeSets = results as IEnumerable<Changeset>;
            if (changeSets != null && changeSets.Count() > 0)
            {
                response += new string('-', 8) + "\t" + new string('-', 50) + "\t" + new string('-', 200);
                foreach (var changeSet in changeSets)
                {
                    var chid= changeSet.ChangesetId.ToString();
                    response += "\n" + chid.PadRight(8) + "\t" + changeSet.Committer.PadRight(50) + "\t" + changeSet.Comment.PadRight(200) + "   \n";
                }
            }
            else
            {
                response += "\n\nNo history entries were found for the item and version combination specified.";
                response += "\nAll files are up to date.\n\n";
            }
            return response;
        }

        private void ConnectTfsServer(TfsParameters tfsParameters)
        {
            if (this.CurrentTfsTeamProjectCollection == null || !this.CurrentTfsTeamProjectCollection.HasAuthenticated)
            {
                var tfsTeamProjectCollection = new TfsTeamProjectCollection(new Uri(tfsParameters.ServerUrl), tfsParameters.Credentials);
                try
                {
                    tfsTeamProjectCollection.Authenticate();
                    this.CurrentTfsTeamProjectCollection = tfsTeamProjectCollection;
                    this.VersionControlServer = tfsTeamProjectCollection.GetService<VersionControlServer>();
                    this.VersionControlServer.NonFatalError += (s, e) => { if (OnNonFatalError != null)OnNonFatalError(s, e); };
                }
                catch (Exception ex)
                {
                    this.CurrentTfsTeamProjectCollection = null;
                    this.VersionControlServer = null;
                    throw new AuthenticationFailureException("TFS Authentication Failed", tfsParameters.ServerUrl, ex);
                }
            }
        }

        private string GetLocalFilePath(string serverProjectPath, Item item, string targetDownloadPath)
        {
            //string relativePath = item.ServerItem.ToLower().Replace(serverProjectPath.ToLower(), "");
            //if (relativePath.StartsWith("/")) relativePath = relativePath.TrimStart(new char[] { '/' });
            string relativePath = item.ServerItem.Replace(serverProjectPath, "");
            if (relativePath.StartsWith("/")) relativePath = relativePath.TrimStart(new char[] { '/' });

            var localFilePath = Path.Combine(targetDownloadPath, relativePath);
            return localFilePath;
        }
    }
}
